package com.cangshi.permission.entity;

import com.alibaba.fastjson.JSON;
import com.cangshi.permission.exception.CheckFailedException;
import com.cangshi.permission.exception.JSONException;
import com.cangshi.permission.exception.LoginFailedException;
import com.cangshi.permission.exception.PermissionInitException;
import com.cangshi.permission.session.entity.Session;
import com.cangshi.permission.session.exception.SessionCannotFindException;
import io.jsonwebtoken.*;

import java.util.Date;
import java.util.Map;
import java.util.Set;

/**
 * Created by Eoly on 2017/4/5.
 */
public class UserSubject implements Subject {

    private ObjectSources objectSources;

    private String authorizationToken;

    private AuthorizationEntity authorizationEntity;

    private Class authorizationEntityClass;

    private Boolean isAuthorizationValue = false;

    private Set<String> roles;

    private Session<String,Object> session;

    public UserSubject(ObjectSources objectSources) {
        this.objectSources = objectSources;
    }

    public UserSubject(
            String authorizationToken,
            Class authorizationEntityClass,
            ObjectSources objectSources
    ) {
        this(objectSources);
        this.authorizationToken = authorizationToken;
        this.authorizationEntityClass = authorizationEntityClass;
        buildAuthorizationEntity();
    }

    public String login(Token token) {

        if (null == objectSources) {
            // 抛错，未在xml中初始化objectSources
            throw new PermissionInitException("服务端未中初始化ObjectSources对象");
        }

        CheckAndBuild checkAndBuild = objectSources.checkAndBuild(token);

        if (checkAndBuild == null) {
            // 抛错，未实现objectSources的checkAndBuild方法
            throw new PermissionInitException("服务端未实现objectSources的checkAndBuild方法");
        }

        Boolean isPass = checkAndBuild.getCheckResult();

        if (null != isPass && isPass) {
            AuthorizationEntity entity = checkAndBuild.getBuildResult();
            if (entity == null) {
                // 抛错，未实现objectSources的checkAndBuild方法
                throw new PermissionInitException("服务端未通过checkAndBuild方法返回一个用户证书实体");
            }
            objectSources.makeEntity(entity);
            return buildAuthorizationToken();
        }

        throw new LoginFailedException("用户验证失败");
    }

    public Boolean isAuthorizationExisted() {
        return this.authorizationToken != null && !this.authorizationToken.equals("");
    }

    public void buildAuthorizationEntity() {
        if (isAuthorizationExisted()) {
            try {
                Claims claims = Jwts.parser().setSigningKey(objectSources.getKeyt())
                        .parseClaimsJws(authorizationToken).getBody();
                this.authorizationEntity = (AuthorizationEntity) JSON.parseObject(
                        claims.getSubject(), authorizationEntityClass);
                this.isAuthorizationValue = true;
            } catch (SignatureException e) {
                // 抛错 验证出错
                throw new CheckFailedException("用户证书签名不合法");
            } catch (MalformedJwtException e) {
                // 抛错
                throw new CheckFailedException("用户证书不合法");
            } finally {
                this.roles = makeRoles();
            }
        } else {
            this.roles = makeRoles();
        }
    }

    public Boolean isAuthorizationLegitimate() {
        if (!isAuthorizationExisted()) {
            return false;
        }
        if (authorizationEntity == null) {
            return false;
        }
        Boolean isLegitimate = objectSources.checkAuthorizationEntity(getEntity());

        // System.out.println("------------>"+isLegitimate);

        if (isLegitimate == null){
            throw new PermissionInitException("服务端未正确设置检查证书的checkAuthorizationEntity方法");
        }

        return isLegitimate;
    }

    public Boolean isAuthorizationWorked() {
        if (!isAuthorizationValue) {
            return false;
        }
        return isAuthorizationLegitimate();
    }

    public Boolean couldAccessing(String url) {

        Boolean defaultIntercepted = objectSources.getDefaultIntercepted();

        if (null == url) {
            return defaultIntercepted;
        }
        // System.out.println("--------===>run here"+JSON.toJSONString(roles));
        if (null == roles) {
            return !defaultIntercepted;
        }

        for (String role : roles) {

            if (!role.matches("/.*/\\*")) {
                continue;
            }

            String pre = role.split("/\\*", 2)[0];

            String regex = pre + "|" + pre + "/.*|"+pre+"\\..*";

            // System.out.println("------------>验证："+regex+url.matches(regex));
            if (url.matches(regex)) {
                return !defaultIntercepted;
            }
        }
        return defaultIntercepted;
    }

    public Set<String> makeRoles() {
        Map<Integer, Set<String>> rolesMap = objectSources.getRolesMap();

        if (!isAuthorizationWorked()) {
            return rolesMap.get(objectSources.getDefaultPermissionKey());
        }

        return rolesMap.get(authorizationEntity.getPermissionKey());
    }

    public String buildAuthorizationToken() {
        AuthorizationEntity entity = objectSources.getEntity();
        if (null == entity) {
            return null;
        }

        // 返回 AuthorizationToken
        return Jwts.builder().setSubject(JSON.toJSONString(entity))
                .setIssuedAt(new Date())
                .signWith(SignatureAlgorithm.HS256, objectSources.getKeyt()).compact();
    }

    public ObjectSources getObjectSources() {
        return objectSources;
    }

    public void setObjectSources(ObjectSources objectSources) {
        this.objectSources = objectSources;
    }

    public AuthorizationEntity getEntity() {
        return this.getAuthorizationEntity();
    }

    public String getAuthorizationToken() {
        return authorizationToken;
    }

    public void setAuthorizationToken(String authorizationToken) {
        this.authorizationToken = authorizationToken;
    }

    public AuthorizationEntity getAuthorizationEntity() {
        return authorizationEntity;
    }

    public void setAuthorizationEntity(AuthorizationEntity authorizationEntity) {
        this.authorizationEntity = authorizationEntity;
    }

    public Class<AuthorizationEntity> getAuthorizationEntityClass() {
        return authorizationEntityClass;
    }

    public void setAuthorizationEntityClass(Class authorizationEntityClass) {
        this.authorizationEntityClass = authorizationEntityClass;
    }

    public Session<String, Object> getSession() {
        if (session == null){
            throw new SessionCannotFindException("用户暂未登录");
        }
        return session;
    }

    public void setSession(Session<String, Object> session) {
        this.session = session;
    }

    public static abstract class CheckAndBuild {


        private Boolean checkResult;
        private AuthorizationEntity buildResult;

        public CheckAndBuild() {
            this.checkResult = setCheckResult();
            this.buildResult = setBuildResult();
        }

        public Boolean getCheckResult() {
            return checkResult;
        }

        protected abstract Boolean setCheckResult();

        public AuthorizationEntity getBuildResult() {
            return buildResult;
        }

        protected abstract AuthorizationEntity setBuildResult();
    }
}
